package trungnt.wipeoff;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.Stage;

import static com.badlogic.gdx.scenes.scene2d.actions.Actions.*;

import trungnt.wipeoff.enums.ButtonEnum;
import trungnt.wipeoff.enums.GameEnum;
import trungnt.wipeoff.model.ButtonActor;
import trungnt.wipeoff.model.GameOverActor;
import trungnt.wipeoff.model.GameState;
import trungnt.wipeoff.model.GridActor;
import trungnt.wipeoff.model.NumberActor;

public class PlayState extends GameState {

	// private GameScreen _game = null;
	private Stage _stage = null;

	private BitmapFont bitmapFont = null;

	private GridActor gridActor = null;
	private NumberActor sampleNumber = null;

	private GameEnum gameEnum = null;

	private List<List<NumberActor>> listNumberActor = null;
	private Vector2 positionList[][] = null;
	List<Vector2> drawScores = null;

	private int[] nextCell = null;
	private int[][] gridValue = null;
	private int selectedRow = 0;
	private int selectedColumn = 0;
	private int animationCnt = 0;

	private int levelProgress = 0;
	private long scores = 0;
	private int multi = 0;
	private int level = 0;

	private Group gameOverGroup = null;

	/**
	 * Khởi tạo giá trị chung cho game
	 */
	public PlayState(GameScreen game, Stage stage) {
		super(game, stage);
		// this._game = game;
		this._stage = this.getStage();

		this._stage.getCamera().translate(0, -100, 0);

		this.bitmapFont = this.getGame().getAssetManager().get("fonts/bmp/diavlo_book_36.fnt", BitmapFont.class);

		this.positionList = new Vector2[Cs.GRID_COLUMN_CNT][Cs.GRID_ROW_CNT];
		for (int i = 0; i < Cs.GRID_COLUMN_CNT; i++) {
			for (int y = 0; y < Cs.GRID_ROW_CNT; y++) {
				this.positionList[i][y] = new Vector2(Cs.GRID_PADDING + Cs.CELL_PADDING / 2 + (i * Cs.CELL_SIZE.x) + (i * Cs.CELL_PADDING),
						Cs.CELL_PADDING / 2 + (Cs.CELL_SIZE.y * y) + (Cs.CELL_PADDING * y));
			}
		}

		this.gridActor = new GridActor(this);
		this._stage.addActor(this.gridActor);

		this.initGame();

		Gdx.input.setInputProcessor(this._stage);
	}

	@Override
	public void resize() {
		this._stage.setViewport(Cs.VW, Cs.VH, true, this.getGame().getViewPort().x, this.getGame().getViewPort().y,
				this.getGame().getViewPort().width, this.getGame().getViewPort().height);

		this._stage.getCamera().translate(0, -100, 0);
	}

	/**
	 * Khởi tạo giá trị cho 1 lần chơi
	 */
	private void initGame() {
		this.drawScores = new ArrayList<Vector2>();

		this.listNumberActor = new ArrayList<List<NumberActor>>();

		for (int i = 0; i < Cs.GRID_COLUMN_CNT; i++) {
			this.listNumberActor.add(new ArrayList<NumberActor>());
		}

		this.nextCell = new int[Cs.GRID_ROW_CNT];
		this.gridValue = new int[Cs.GRID_COLUMN_CNT][Cs.GRID_ROW_CNT];

		this.createSampleNumber();

		this.gameEnum = GameEnum.NORMAL;

		this.levelProgress = Cs.LEVEL_PROGRESS;
		this.scores = 0;
		this.level = 1;
		this.multi = 0;
	}

	private void createSampleNumber() {
		this.sampleNumber = new NumberActor(this, this.GetRandomNumber(Cs.GRID_COLUMN_CNT + 5) );
		this.sampleNumber.setPosition(this.positionList[3][6].x, this.positionList[3][6].y + 200);
		this._stage.addActor(sampleNumber);
	}

	@Override
	public void dispose() {
		this._stage.dispose();
		this.bitmapFont.dispose();
	}

	@Override
	public void render(float delta) {
		// Gdx.app.log("TrungNT", "PlayState state rendering...");

		this._stage.act(delta);
		this._stage.draw();

		this.getGame().getSpriteBatch().begin();
		this.bitmapFont.draw(this.getGame().getSpriteBatch(), "Level: " + this.level + " ( " + this.levelProgress + " )", 50, 1200);
		this.bitmapFont.draw(this.getGame().getSpriteBatch(), "" + this.scores, 500, 1200);

		for (Vector2 v : this.drawScores) {
			this.bitmapFont.draw(this.getGame().getSpriteBatch(), "" + (10 * this.multi), v.x, v.y + 100);
		}
		this.getGame().getSpriteBatch().end();

		switch (this.gameEnum) {
			case NORMAL:

				// Gdx.graphics.setContinuousRendering(false);

				break;
			case GAMEOVER:

				// Gdx.graphics.setContinuousRendering(false);

				break;

			case MOVING_SAMPLE_NUMBER:

				// Sau khi di chuyển số mẫu xong,
				// thì add nó vào list để quản lý việc xóa, di chuyển lên xuống
				if (this.sampleNumber.getActions().size == 0) {

					this.levelProgress--;

					// Add vào list
					this.listNumberActor.get(this.selectedColumn).add(this.sampleNumber);

					// Kiểm tra rule game (kiểm tra lần đầu nền không cần phải
					// lặp nguyên grid)
					checkGame(false);
				}

				break;

			case NUMBER_DISAPPEARING:

				// Sau khi xóa số thì di chuyển các số ở trên xuống dưới
				if (this.animationCnt == 0) {
					this.movingNumber();

					this.gameEnum = GameEnum.MOVING_NUMBER;
				}

				break;

			case MOVING_NUMBER:

				// Sau khi di chuyển các số ở trên xuống dưới thì kiểm tra lại,
				// lần này lặp nguyên grid
				if (this.animationCnt == 0) {
					checkGame(true);
				}

				break;

			default:
				break;
		}

	}

	/**
	 * Lưu vị trí đã chọn, set giá trị cho grid sau đó di chuyển số mẫu vào vị
	 * trí đã chọn.
	 * 
	 * @param column
	 */
	public void movingSampleNumber(int column) {
		this.selectedColumn = column;
		this.selectedRow = this.nextCell[this.selectedColumn];

		this.nextCell[this.selectedColumn]++;
		this.gridValue[this.selectedColumn][this.selectedRow] = this.sampleNumber.getValue();

		this.sampleNumber.setIndex(this.selectedColumn, this.selectedRow);

		this.gameEnum = GameEnum.MOVING_SAMPLE_NUMBER;

		// Gdx.graphics.setContinuousRendering(true);
		float diffx = this.sampleNumber.getX() - this.positionList[this.selectedColumn][this.selectedRow].x;
		if (diffx < 0)
			diffx *= -1;
		float diffy = this.sampleNumber.getY() - this.positionList[this.selectedColumn][this.selectedRow].y;
		if (diffy < 0)
			diffy *= -1;

		float time1 = diffx * Cs.MOVING_SAMPLE_SPEED;
		float time2 = diffy * Cs.MOVING_SAMPLE_SPEED;

		this.sampleNumber
				.addAction(sequence(moveTo(this.positionList[this.selectedColumn][this.selectedRow].x, this.sampleNumber.getY(), time1),
						moveTo(this.positionList[this.selectedColumn][this.selectedRow].x,
								this.positionList[this.selectedColumn][this.selectedRow].y, time2)));

	}

	/**
	 * Di chuyển các ở trên số vừa bị xóa xuống dưới
	 */
	public void movingNumber() {
		for (List<NumberActor> na : this.listNumberActor) {
			for (int row = 0; row < na.size(); row++) {
				NumberActor actor = na.get(row);

				if (row != actor.getRowIndex()) {
					int colIndex = actor.getColIndex();

					actor.setIndex(colIndex, row);
					this.animationCnt++;

					float diffy = actor.getY() - this.positionList[colIndex][row].y;
					if (diffy < 0)
						diffy *= -1;

					actor.addAction(sequence(moveTo(this.positionList[colIndex][row].x, this.positionList[colIndex][row].y, diffy
							* Cs.MOVING_NUMBER_SPEED), run(new Runnable() {
						public void run() {
							animationCnt--;
						}
					})));
				}
			}
		}
	}

	/**
	 * Tạo số ngẫu nhiên
	 * 
	 * @param range
	 *            Phạm vi tạo số ngẫu nhiên (7 thì sẽ random từ 0 đến 6)
	 * @return giá trị ngẫu nhiên được tạo ra
	 */
	private int GetRandomNumber(int range) {
		Random rand = new Random();

		int a = rand.nextInt(range);
		
		if (a > 6)
		{
			a = 7;
		}
		
		return a + 1;
	}

	private void checkGame(boolean needLoop) {
		this.multi++;

		boolean result1, result2;

		result1 = this.checkDoc(needLoop);
		result2 = this.checkNgang();

		if (result1 || result2) {
			this.resetGridValueAfterCheck();

			this.movingValueDown();

			this.gameEnum = GameEnum.NUMBER_DISAPPEARING;
		}
		else {
			for (int i : this.nextCell) {
				if (i == Cs.GRID_ROW_CNT) {
					this.gameEnum = GameEnum.GAMEOVER;

					Log.writeLog("GAMEOVER");

					gameOverGroup = new Group();

					GameOverActor gameover = new GameOverActor(this);
					gameOverGroup.addActor(gameover);

					ButtonActor replay = new ButtonActor(this, ButtonEnum.REPLAY);
					ButtonActor menu = new ButtonActor(this, ButtonEnum.MENU);

					gameOverGroup.addActor(replay);
					gameOverGroup.addActor(menu);

					this._stage.addActor(gameOverGroup);

					Gdx.input.setInputProcessor(this._stage);

					return;
				}
			}

			if (this.levelProgress == 0) {
				this.levelProgress = Cs.LEVEL_PROGRESS;
				this.level++;
				this.scores += 1000;

				this.insertNumber();

				this.movingNumber();

				this.gameEnum = GameEnum.MOVING_NUMBER;

				return;
			}

			Gdx.input.setInputProcessor(this._stage);
			this.createSampleNumber();

			this.gameEnum = GameEnum.NORMAL;

			this.multi = 0;
		}
	}

	private void movingValueDown() {
		for (int i = Cs.GRID_ROW_CNT - 2; i >= 0; i--) {
			for (int y = 0; y < Cs.GRID_COLUMN_CNT; y++) {
				if (this.gridValue[y][i] == 0 && this.gridValue[y][i + 1] != 0) {
					for (int x = i + 1; x < Cs.GRID_ROW_CNT; x++) {
						this.gridValue[y][x - 1] = Integer.valueOf(this.gridValue[y][x]);
						this.gridValue[y][x] = 0;
					}
				}
			}
		}
	}

	private void resetGridValueAfterCheck() {
		for (int col = 0; col < this.listNumberActor.size(); col++) {
			for (int row = 0; row < this.listNumberActor.get(col).size(); row++) {
				if (this.listNumberActor.get(col).get(row).getRemoved()) {
					this.gridValue[this.listNumberActor.get(col).get(row).getColIndex()][this.listNumberActor.get(col).get(row).getRowIndex()] = 0;
					this.listNumberActor.get(col).remove(row);
					this.nextCell[col]--;
					row--;
				}
			}
		}
	}

	private void phaGach(int col, int row) {
		NumberActor tmp = this.listNumberActor.get(col).get(row);

		if (tmp.getValue() == 8 && !tmp.getJustChangedTo9()) {
			tmp.setTexture(9);
			this.gridValue[col][row] = 9;
			Log.writeLog("Set cell(" + col + " - " + row + " thanh 9"); 
		}
		else if (tmp.getValue() == 9 && !tmp.getJustChangedTo9()) {
			int a = this.GetRandomNumber(Cs.GRID_COLUMN_CNT);
			tmp.setTexture(a);
			this.gridValue[col][row] = a;
			Log.writeLog("Set cell(" + col + " - " + row + " thanh " + a);
		}
	}

	/**
	 * Kiem tra hang doc
	 * 
	 * @param newSts
	 * @return
	 */
	private boolean checkDoc(boolean needLoop) {
		boolean result = false;

		if (needLoop) {
			for (int i = 0; i < Cs.GRID_COLUMN_CNT; i++) {
				boolean notification = false;

				for (int y = 0; y < this.nextCell[i]; y++) {
					if (this.gridValue[i][y] > 1 && this.gridValue[i][y] == this.nextCell[i]) {
						result = true;
						notification = true;

						int col = i;
						int row = y;
						
						if(this.nextCell[col] > 1)
						{
							if (row == 0) {
								phaGach(col, row + 1);
							}
							
							else if (row == this.nextCell[col] - 1) {
								phaGach(col, row - 1);
							}
							else
							{
								phaGach(col, row + 1);
								phaGach(col, row - 1);
							}
						}
						
						if(col == 0)
						{
							if(this.nextCell[col+1]>row)
							{
								phaGach(col+1, row);
							}
						}
						
						else if(col == Cs.GRID_COLUMN_CNT - 1)
						{
							if(this.nextCell[col-1]>row)
							{
								phaGach(col-1, row);
							}
						}
						else
						{
							if(this.nextCell[col+1]>row)
							{
								phaGach(col+1, row);
							}
							if(this.nextCell[col-1]>row)
							{
								phaGach(col-1, row);
							}
						}
						
						final NumberActor tmp = this.listNumberActor.get(i).get(y);
						tmp.setRemoved(true);

						if (tmp.getActions().size == 0) {
							this.animationCnt++;
							final Vector2 v = new Vector2(tmp.getX(), tmp.getY());
							drawScores.add(v);

							this.scores += 10 * this.multi;

							tmp.addAction(sequence(scaleBy(-1, -1, Cs.DISAPPEARING_SPEED), run(new Runnable() {
								public void run() {
									tmp.remove();

									animationCnt--;
									drawScores.remove(v);
								}
							})));
						}

						Log.writeLog("1.Pha cot " + i + ", hang " + y);
					}
				}

				if (notification) {
					float x, y, width, height;

					x = this.positionList[i][0].x;
					y = 0;
					width = Cs.CELL_SIZE.x;
					height = Cs.CELL_SIZE.y * (this.nextCell[i]);

					// this.notificationBg.add(new float[] { x - 5, y - 5, width
					// + 10, height + 10 });
				}
			}
		}
		else {
			boolean notification = false;

			for (int i = 0; i <= this.selectedRow; i++) {
				if (this.gridValue[this.selectedColumn][i] > 1 && this.gridValue[this.selectedColumn][i] == this.selectedRow + 1) {
					result = true;
					notification = true;
					
					int col = this.selectedColumn;
					int row = i;

					if(this.nextCell[col] > 1)
					{
						if (row == 0) {
							phaGach(col, row + 1);
						}
						
						else if (row == this.nextCell[col] - 1) {
							phaGach(col, row - 1);
						}
						else
						{
							phaGach(col, row + 1);
							phaGach(col, row - 1);
						}
					}
					
					if(col == 0)
					{
						if(this.nextCell[col+1]>row)
						{
							phaGach(col+1, row);
						}
					}
					
					else if(col == Cs.GRID_COLUMN_CNT - 1)
					{
						if(this.nextCell[col-1]>row)
						{
							phaGach(col-1, row);
						}
					}
					else
					{
						if(this.nextCell[col+1]>row)
						{
							phaGach(col+1, row);
						}
						if(this.nextCell[col-1]>row)
						{
							phaGach(col-1, row);
						}
					}

					final NumberActor tmp = this.listNumberActor.get(this.selectedColumn).get(i);
					tmp.setRemoved(true);

					if (tmp.getActions().size == 0) {
						this.animationCnt++;

						final Vector2 v = new Vector2(tmp.getX(), tmp.getY());
						drawScores.add(v);

						this.scores += 10 * this.multi;

						tmp.addAction(sequence(scaleBy(-1, -1, Cs.DISAPPEARING_SPEED), run(new Runnable() {
							public void run() {
								tmp.remove();

								animationCnt--;
								drawScores.remove(v);
							}
						})));
					}

					Log.writeLog("2.Pha cot " + this.selectedColumn + ", hang " + i);
				}
			}

			if (notification) {
				float x, y, width, height;

				x = this.positionList[this.selectedColumn][this.selectedRow].x;
				y = 0;
				width = Cs.CELL_SIZE.x;
				height = Cs.CELL_SIZE.y * (this.selectedRow + 1);

				// this.notificationBg.add(new float[] { x - 5, y - 5, width +
				// 10, height + 10 });
			}
		}

		return result;
	}

	private boolean checkNgang() {
		boolean result = false;

		// Lặp theo số lượng hàng của lưới
		for (int i = 0; i < Cs.GRID_ROW_CNT; i++) {
			int count = 0;
			int indexToCheck = 0;
			boolean needCheck = false;

			// Lặp theo số lượng cột của lưới
			for (int y = 0; y < Cs.GRID_COLUMN_CNT; y++) {

				/*
				 * Trường hợp chưa kiểm tra và giá trị của cell hiện tại khác 0,
				 * bật cờ để kiểm tra khi gặp cell trống, đánh dấu index bắt đầu
				 * tại cell hiện tại
				 */
				if (this.gridValue[y][i] != 0) {
					if (!needCheck) {
						needCheck = true;
						indexToCheck = y;
					}

					// Cộng dồn số lượng các cell có giá trị
					count++;

					/*
					 * Nếu cell hiện tại nằm trong cột cuối cùng và có giá trị,
					 * thực hiện kiểm tra. Đây là lần kiểm tra cuối cùng của row
					 * hiện tại.
					 */
					if (y == Cs.GRID_COLUMN_CNT - 1) {
						boolean notification = false;

						for (int z = indexToCheck; z <= y; z++) {
							if (this.gridValue[z][i] == count) {
								result = true;
								notification = true;

								final NumberActor tmp = this.listNumberActor.get(z).get(i);
								tmp.setRemoved(true);
								
								int col = z;
								int row = i;

								if(this.nextCell[col] > 1)
								{
									if (row == 0) {
										phaGach(col, row + 1);
									}
									
									else if (row == this.nextCell[col] - 1) {
										phaGach(col, row - 1);
									}
									else
									{
										phaGach(col, row + 1);
										phaGach(col, row - 1);
									}
								}
								
								if(col == 0)
								{
									if(this.nextCell[col+1]>row)
									{
										phaGach(col+1, row);
									}
								}
								
								else if(col == Cs.GRID_COLUMN_CNT - 1)
								{
									if(this.nextCell[col-1]>row)
									{
										phaGach(col-1, row);
									}
								}
								else
								{
									if(this.nextCell[col+1]>row)
									{
										phaGach(col+1, row);
									}
									if(this.nextCell[col-1]>row)
									{
										phaGach(col-1, row);
									}
								}

								if (tmp.getActions().size == 0) {
									this.animationCnt++;

									final Vector2 v = new Vector2(tmp.getX(), tmp.getY());
									drawScores.add(v);

									this.scores += 10 * this.multi;

									tmp.addAction(sequence(scaleBy(-1, -1, Cs.DISAPPEARING_SPEED), run(new Runnable() {
										public void run() {
											tmp.remove();

											animationCnt--;
											drawScores.remove(v);
										}
									})));
								}
								Log.writeLog("3.Pha hang " + i + ", cot " + z);
							}
						}

						if (notification) {
							float x, yy, width, height;

						}
					}

				}
				else {
					if (needCheck) {
						boolean notification = false;

						for (int z = indexToCheck; z < y; z++) {
							if (this.gridValue[z][i] == count) {
								Log.writeLog("4.Pha hang " + i + ", cot " + z);
								result = true;
								notification = true;

								final NumberActor tmp = this.listNumberActor.get(z).get(i);
								tmp.setRemoved(true);
								
								int col = z;
								int row = i;

								if(this.nextCell[col] > 1)
								{
									if (row == 0) {
										phaGach(col, row + 1);
									}
									
									else if (row == this.nextCell[col] - 1) {
										phaGach(col, row - 1);
									}
									else
									{
										phaGach(col, row + 1);
										phaGach(col, row - 1);
									}
								}
								
								if(col == 0)
								{
									if(this.nextCell[col+1]>row)
									{
										phaGach(col+1, row);
									}
								}
								
								else if(col == Cs.GRID_COLUMN_CNT - 1)
								{
									if(this.nextCell[col-1]>row)
									{
										phaGach(col-1, row);
									}
								}
								else
								{
									if(this.nextCell[col+1]>row)
									{
										phaGach(col+1, row);
									}
									if(this.nextCell[col-1]>row)
									{
										phaGach(col-1, row);
									}
								}

								if (tmp.getActions().size == 0) {
									this.animationCnt++;

									final Vector2 v = new Vector2(tmp.getX(), tmp.getY());
									drawScores.add(v);

									this.scores += 10 * this.multi;

									tmp.addAction(sequence(scaleBy(-1, -1, Cs.DISAPPEARING_SPEED), run(new Runnable() {
										public void run() {
											tmp.remove();

											animationCnt--;
											drawScores.remove(v);
										}
									})));
								}
							}
						}

						if (notification) {
							float x, yy, width, height;
						}

						needCheck = false;
						count = 0;
					}
				}
			}
		}

		return result;
	}

	public void insertNumber() {
		for (int i = Cs.GRID_ROW_CNT - 1; i >= 0; i--) {
			for (int y = 0; y < Cs.GRID_COLUMN_CNT; y++) {
				if (i == 0) {
					int value = this.GetRandomNumber(Cs.GRID_COLUMN_CNT + 5);

					NumberActor tmp = new NumberActor(this, value);
					tmp.setPosition(this.positionList[y][0].x, this.positionList[y][0].y);
					tmp.setIndex(y, 0);
					this._stage.addActor(tmp);

					this.listNumberActor.get(y).add(0, tmp);

					this.gridValue[y][i] = value;

					// Set row cho gạch kế tiếp
					this.nextCell[y]++;
				}
				else {
					this.gridValue[y][i] = this.gridValue[y][i - 1];
				}
			}
		}
	}

	private void showValue() {
		for (int y = Cs.GRID_ROW_CNT - 1; y >= 0; y--) {
			String s = "";
			for (int i = 0; i < Cs.GRID_COLUMN_CNT; i++) {
				String t = this.gridValue[i][y] != 0 ? ((this.gridValue[i][y]) + "") : "-";
				s += t + "     ";
			}

			Gdx.app.log("TrungNT", s);
		}

		Gdx.app.log("TrungNT", "=================================================");
	}

	@Override
	public void startAnimation(ButtonEnum buttonEnum) {
		// Animation here

		if (buttonEnum == ButtonEnum.REPLAY) {
			for (List<NumberActor> na : this.listNumberActor) {
				for (NumberActor a : na) {
					a.remove();
				}
			}

			this.gameOverGroup.remove();

			this.initGame();
		}
		else {
			this.getGame().processMenuButton(buttonEnum);
		}
	}
}
